﻿using System.Collections.Generic;
using System.Data.Objects.SqlClient;
using System.Linq;
using NRank.Algorithms.Strategies;
using NRank.Common;

namespace NRank.Benchmark.Queries
{
	/// <summary>
	/// Implements benchmark query: Simple join of two tables (Towns join clubs)
	/// </summary>
	public class SimpleJoinQuerySet : BenchmarkQuerySet
	{
		protected override IEnumerable<IRanked<JoinResult>> HashRankJoinRoundRobinTest(NRankBenchmarkDbDataContext cx)
		{
			return cx.Towns
				.OrderByDescending(t => t.Rating)
				.HashRankJoin(
					cx.Clubs.OrderByDescending(c => c.Rating),
					t => t.Rating,
					c => c.Rating,
					t => t.IdTown,
					c => c.IdTown, (t, c) => new JoinResult {IdClub = c.IdClub, IdTown = t.IdTown}, PullingStrategyTypes.Alternating,
					AggregateFunc);
		}

		protected override IEnumerable<IRanked<JoinResult>> HashRankJoinAdaptiveTest(NRankBenchmarkDbDataContext cx)
		{
			return cx.Towns
				.OrderByDescending(t => t.Rating)
				.HashRankJoin(
					cx.Clubs.OrderByDescending(c => c.Rating),
					t => t.Rating,
					c => c.Rating,
					t => t.IdTown,
					c => c.IdTown, (t, c) => new JoinResult {IdClub = c.IdClub, IdTown = t.IdTown}, PullingStrategyTypes.MinimizeThreshold,
					AggregateFunc);
		}

		protected override IEnumerable<IRanked<JoinResult>> RankJoinTestRoundRobin(NRankBenchmarkDbDataContext cx)
		{
			return cx.Towns
				.OrderByDescending(t => t.Rating)
				.RankJoin(
					cx.Clubs.OrderByDescending(c => c.Rating),
					t => t.Rating,
					c => c.Rating,
					(t, c) => new JoinResult {IdClub = c.IdClub, IdTown = t.IdTown},
					(t, c) => t.IdTown == c.IdTown,
					PullingStrategyTypes.Alternating, 
					AggregateFunc);
		}

		protected override IEnumerable<IRanked<JoinResult>> RankJoinTestAdaptive(NRankBenchmarkDbDataContext cx)
		{
			return cx.Towns
				.OrderByDescending(t => t.Rating)
				.RankJoin(
					cx.Clubs.OrderByDescending(c => c.Rating),
					t => t.Rating,
					c => c.Rating,
					(t, c) => new JoinResult {IdClub = c.IdClub, IdTown = t.IdTown},
					(t, c) => t.IdTown == c.IdTown,
					PullingStrategyTypes.MinimizeThreshold,
					AggregateFunc);
		}

		protected override IEnumerable<IRanked<JoinResult>> NestedLoopJoinTest(NRankBenchmarkDbDataContext cx)
		{
			return cx.Clubs.OrderByDescending(c => c.Rating)
				.NestedLoopRankJoin(
					cx.Towns,
					c => c.Rating,
					t => t.Rating,
					(c, t) => new JoinResult {IdClub = c.IdClub, IdTown = t.IdTown},
					(c, t) => c.IdTown == t.IdTown,
					AggregateFunc);
		}

		protected override IEnumerable<IRanked<JoinResult>> NestedLoopJoinEqTest(NRankBenchmarkDbDataContext cx)
		{
			return cx.Clubs.OrderByDescending(c => c.Rating)
				.NestedLoopRankJoin(
					cx.Towns,
					c => c.Rating,
					t => t.Rating,
					(c, t) => new JoinResult {IdClub = c.IdClub, IdTown = t.IdTown},
					c => c.IdTown,
					t => t.IdTown,
					AggregateFunc);
		}

		protected override IEnumerable<IRanked<JoinResult>> TraditionalQuery(NRankBenchmarkDbDataContext cx)
		{
			return cx.Towns
				.Join(cx.Clubs,
				      t => t.IdTown,
				      c => c.IdTown,
				      (t, c) => new {c.IdClub, t.IdTown, Rating = c.Rating + t.Rating})
				.OrderByDescending(n => n.Rating)
				.Select(n => new JoinResult { IdClub = n.IdClub, IdTown = n.IdTown, Rating = n.Rating })
				.AsEnumerable()
				.AsRanked(n => n.Rating);
		}
	}
}